using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace ASMdll
{
	internal class Operand
	{
		public enum AddressingType
		{
			Register,
			Immediate,
			Pointer,
			RegisterImplicit,
			Based,
			PointerWithIndexing
		}

		[Flags]
		public enum Register : int
		{
			AL = (1 << 0), AX = (1 << 1),
			CL = (1 << 2), CX = (1 << 3),
			DL = (1 << 4), DX = (1 << 5),
			BL = (1 << 6), BX = (1 << 7),
			AH = (1 << 8), SP = (1 << 9),
			CH = (1 << 10), BP = (1 << 11),
			DH = (1 << 12), SI = (1 << 13),
			BH = (1 << 14), DI = (1 << 15),
			ES = (1 << 16),
			CS = (1 << 17),
			SS = (1 << 18),
			DS = (1 << 19),
			FS = (1 << 20),
			GS = (1 << 21)
		}

		public static Size getSize(Register r)
		{
			if (r == Register.AL || r == Register.AH)
				return Size.Byte;
			if (r == Register.BL || r == Register.BH)
				return Size.Byte;
			if (r == Register.CL || r == Register.CH)
				return Size.Byte;
			if (r == Register.DL || r == Register.DH)
				return Size.Byte;
			return Size.Word;
		}

		public static Byte getRegBits(Register r)
		{
			if (r == Register.AL || r == Register.AX)
				return 0;
			if (r == Register.CL || r == Register.CX)
				return 1;
			if (r == Register.DL || r == Register.DX)
				return 2;
			if (r == Register.BL || r == Register.BX)
				return 3;
			if (r == Register.AH || r == Register.SP)
				return 4;
			if (r == Register.CH || r == Register.BP)
				return 5;
			if (r == Register.DH || r == Register.SI)
				return 6;
			if (r == Register.BH || r == Register.DI)
				return 7;

			throw new Exception("Internal complier error in getting register bits!");
		}
		public static Byte getSregBits(Register r)
		{
			if (r == Register.ES)
				return 0;
			if (r == Register.CS)
				return 1;
			if (r == Register.SS)
				return 2;
			if (r == Register.DS)
				return 3;
			if (r == Register.FS)
				return 4;
			if (r == Register.GS)
				return 5;

			throw new Exception("Internal complier error in getting register bits!");
		}

		public static Register GetRegister(string reg)
		{
			switch (reg.ToUpper())
			{
				case "AL": return Register.AL;
				case "AX": return Register.AX;
				case "CL": return Register.CL;
				case "CX": return Register.CX;
				case "DL": return Register.DL;
				case "DX": return Register.DX;
				case "BL": return Register.BL;
				case "BX": return Register.BX;
				case "AH": return Register.AH;
				case "SP": return Register.SP;
				case "CH": return Register.CH;
				case "BP": return Register.BP;
				case "DH": return Register.DH;
				case "SI": return Register.SI;
				case "BH": return Register.BH;
				case "DI": return Register.DI;
				case "ES": return Register.ES;
				case "CS": return Register.CS;
				case "SS": return Register.SS;
				case "DS": return Register.DS;
				case "FS": return Register.FS;
				case "GS": return Register.GS;
			}

			throw new Exception("Internal compiler error in getting register from string!");
		}

		public static Byte GetModRMByte(Operand op)
		{
			if (op.AT == AddressingType.Immediate)
				throw new Exception("Internal compiler error in gettin ModRM Byte!");

			if (op.AT == AddressingType.Register)
				return (Byte)((3 << 6) | getRegBits(op.R));

			if (op.AT == AddressingType.Pointer)
				return 6;

			if (op.AT == AddressingType.RegisterImplicit)
			{
				if(op.IsInd)
				{
					if (op.R == Register.BX && op.Ind == Register.SI)
						return 0;
					if (op.R == Register.BX && op.Ind == Register.DI)
						return 1;
					if (op.R == Register.BP && op.Ind == Register.SI)
						return 2;
					if (op.R == Register.BP && op.Ind == Register.DI)
						return 3;
				}
				else
				{
					if (op.R == Register.SI)
						return 4;
					if (op.R == Register.DI)
						return 5;
					if (op.R == Register.BX)
						return 7;
				}
				throw new Exception("Invalid register in operand!");
			}

			if (op.AT == AddressingType.Based)
			{
				if (op.IsInd)
				{
					if (op.R == Register.BX && op.Ind == Register.SI)
						return 128 | 0;
					if (op.R == Register.BX && op.Ind == Register.DI)
						return 128 | 1;
					if (op.R == Register.BP && op.Ind == Register.SI)
						return 128 | 2;
					if (op.R == Register.BP && op.Ind == Register.DI)
						return 128 | 3;
				}
				else
				{
					if (op.R == Register.SI)
						return 128 | 4;
					if (op.R == Register.DI)
						return 128 | 5;
					if (op.R == Register.BP)
						return 128 | 6;
					if (op.R == Register.BX)
						return 128 | 7;
				}
				throw new Exception("Invalid register in operand!");
			}

			if (op.AT == AddressingType.PointerWithIndexing)
			{
				if (op.R == Register.SI)
					return 128 | 4;
				if (op.R == Register.DI)
					return 128 | 5;
				if (op.R == Register.BP)
					return 128 | 6;
				if (op.R == Register.BX)
					return 128 | 7;

				throw new Exception("Invalid register in operand!");
			}

			throw new Exception("Internal compiler error!");
		}

		public enum Size
		{
			Byte,
			Word,
			Undefined
		}

		private AddressingType at;
		private Register r;
		private Register ind;
		private bool isInd;
		private UInt16 im;
		private string variableName;
		private SymTabElement.SymType symType;
		private UInt16 offset;
		private bool isOffset;
		private Size size;

		public Operand(string op)
		{
			Regex registerRegex = new Regex(@"^\s*(?<reg>AL|AX|CL|CX|DL|DX|BL|BX|AH|SP|CH|BP|DH|SI|BH|DI|ES|CS|SS|DS|FS|GS)\s*$", RegexOptions.IgnoreCase);
			Regex immediateRegex = new Regex(@"^\s*(?<size>(BYTE|WORD))?\s*((?<hex>[0-9][0-9A-F]*)H|(?<dec>[0-9]+)|(?<dec1>[0-9]+)D|(?<bin>[0-1]+)B)\s*$", RegexOptions.IgnoreCase);
			Regex pointerRegex = new Regex(@"^\s*(?<name>[_A-Z][_A-Z0-9]*)\s*$", RegexOptions.IgnoreCase);
			Regex registerImplicitRegex = new Regex(@"^\s*\[\s*(?<reg>AL|AX|CL|CX|DL|DX|BL|BX|AH|SP|CH|BP|DH|SI|BH|DI|ES|CS|SS|DS|FS|GS)\s*\]\s*(\[\s*(?<ind>AL|AX|CL|CX|DL|DX|BL|BX|AH|SP|CH|BP|DH|SI|BH|DI|ES|CS|SS|DS|FS|GS)\s*\])?\s*$", RegexOptions.IgnoreCase);
			Regex basedRegex = new Regex(@"^\s*\[\s*(?<reg>AL|AX|CL|CX|DL|DX|BL|BX|AH|SP|CH|BP|DH|SI|BH|DI|ES|CS|SS|DS|FS|GS)\s*\]\s*(\[\s*(?<ind>AL|AX|CL|CX|DL|DX|BL|BX|AH|SP|CH|BP|DH|SI|BH|DI|ES|CS|SS|DS|FS|GS)\s*\])?\s*\+\s*((?<hex>[0-9][0-9A-F]*)H|(?<dec>[0-9]+)|(?<dec1>[0-9]+)D|(?<bin>[0-1]+)B)\s*$", RegexOptions.IgnoreCase);
			Regex pointerWithIndexingRegex = new Regex(@"(^\s*(?<name>[_a-zA-Z][_a-zA-Z0-9]*)\s*\[\s*(?<reg>AL|AX|CL|CX|DL|DX|BL|BX|AH|SP|CH|BP|DH|SI|BH|DI|ES|CS|SS|DS|FS|GS)\s*\]\s*$|^\s*(?<reg>AL|AX|CL|CX|DL|DX|BL|BX|AH|SP|CH|BP|DH|SI|BH|DI|ES|CS|SS|DS|FS|GS)\s*\+(?<name>[_A-Z][_A-Z0-9]*)\s*$)", RegexOptions.IgnoreCase);

			Match m = registerRegex.Match(op);
			if (m.Success)
			{
				at = AddressingType.Register;
				r = GetRegister(m.Groups["reg"].Value);
				return;
			}

			m = immediateRegex.Match(op);
			if (m.Success)
			{
				at = AddressingType.Immediate;
				if (m.Groups["hex"].Value != String.Empty)
					im = MyConverter.HexToUInt16(m.Groups["hex"].Value);
				if (m.Groups["dec"].Value != String.Empty)
					im = MyConverter.DecToUInt16(m.Groups["dec"].Value);
				if (m.Groups["dec1"].Value != String.Empty)
					im = MyConverter.DecToUInt16(m.Groups["dec1"].Value);
				if (m.Groups["bin"].Value != String.Empty)
					im = MyConverter.BinToUInt16(m.Groups["bin"].Value);
				if (m.Groups["size"].Value != String.Empty)
					size = m.Groups["size"].Value.ToUpper() == "BYTE" ? Size.Byte : Size.Word;
				else
					size = Size.Undefined;
				return;
			}

			m = pointerRegex.Match(op);
			if (m.Success)
			{
				at = AddressingType.Pointer;
				variableName = m.Groups["name"].Value.ToUpper();
				return;
			}

			m = registerImplicitRegex.Match(op);
			if (m.Success)
			{
				at = AddressingType.RegisterImplicit;
				r = GetRegister(m.Groups["reg"].Value);
				if (m.Groups["ind"].Value != String.Empty)
				{
					isInd = true;
					ind = GetRegister(m.Groups["ind"].Value);
				}
				else
					isInd = false;
				return;
			}

			m = basedRegex.Match(op);
			if (m.Success)
			{
				at = AddressingType.Based;
				r = GetRegister(m.Groups["reg"].Value);
				if (m.Groups["ind"].Value != String.Empty)
				{
					isInd = true;
					ind = GetRegister(m.Groups["ind"].Value);
				}
				else
					isInd = false;
				if (m.Groups["hex"].Value != String.Empty)
					offset = MyConverter.HexToUInt16(m.Groups["hex"].Value);
				if (m.Groups["dec"].Value != String.Empty)
					offset = MyConverter.DecToUInt16(m.Groups["dec"].Value);
				if (m.Groups["dec1"].Value != String.Empty)
					offset = MyConverter.DecToUInt16(m.Groups["dec1"].Value);
				if (m.Groups["bin"].Value != String.Empty)
					offset = MyConverter.BinToUInt16(m.Groups["bin"].Value);
				return;
			}

			m = pointerWithIndexingRegex.Match(op);
			if (m.Success)
			{
				at = AddressingType.PointerWithIndexing;
				variableName = m.Groups["name"].Value.ToUpper();
				r = GetRegister(m.Groups["reg"].Value);
				return;
			}

			throw new Exception("Unknown operand type!");
		}

		public AddressingType AT
		{
			get { return at; }
		}

		public Register R
		{
			get { return r; }
		}

		public UInt16 IM
		{
			get { return im; }
		}

		public string VariableName
		{
			get { return variableName; }
		}

		public UInt16 Offset
		{
			get { return offset; }
		}

		public Size Size_
		{
			get { return size; }
		}

		public SymTabElement.SymType SymType_
		{
			get { return symType; }
			set { symType = value; }
		}

		public bool IsInd
		{
			get { return isInd; }
		}

		public Register Ind
		{
			get { return ind; }
		}
	}
}
